<?php
/**
 * SocialEngine
 *
 * @category   Application_Extensions
 * @package    Vote
 * @copyright  Copyright KEENONWEB LLC
 * @license    http://www.keenonweb.com/
 * @version    $Id: Encode.php 2011-03-09 08:45:41Z idris $
 * @author     Idris
 */

/**
 * @category   Application_Extensions
 * @package    Vote
 * @copyright  Copyright KEENONWEB LLC
 * @license    http://www.keenonweb.com/
 */

class Vote_Plugin_Job_Encode extends Core_Plugin_Job_Abstract {
	protected function _execute() {
		// Get job and params
		$job = $this->getJob ();
		
		// No video id?
		if (! ($participant_id = $this->getParam ( 'participant_id' ))) {
			$this->_setState ( 'failed', 'No video identity provided.' );
			$this->_setWasIdle ();
			return;
		}
		
		// Get video object
		$part = Engine_Api::_ ()->getItem ( 'vote_participant', $participant_id );
		if (! $part || ! ($part instanceof Vote_Model_Participant)) {
			$this->_setState ( 'failed', 'Video is missing.' );
			$this->_setWasIdle ();
			return;
		}
		
		// Check video status
		if (0 != $part->status) {
			$this->_setState ( 'failed', 'Video has already been encoded, or has already failed encoding.' );
			$this->_setWasIdle ();
			return;
		}
		
		// Process
		try {
			$this->_process ( $part );
			$this->_setIsComplete ( true );
		} catch ( Exception $e ) {
			$this->_setState ( 'failed', 'Exception: ' . $e->getMessage () );
		}
	}
	
	protected function _process($part) {
		// Make sure FFMPEG path is set
		$ffmpeg_path = Engine_Api::_ ()->getApi ( 'settings', 'core' )->video_ffmpeg_path;
		if (! $ffmpeg_path) {
			throw new Exception ( 'Ffmpeg not configured' );
		}
		// Make sure FFMPEG can be run
		if (! @file_exists ( $ffmpeg_path ) || ! @is_executable ( $ffmpeg_path )) {
			$output = null;
			$return = null;
			exec ( $ffmpeg_path . ' -version', $output, $return );
			if ($return > 0) {
				throw new Exception ( 'Ffmpeg found, but is not executable' );
			}
		}
		
		// Check we can execute
		if (! function_exists ( 'shell_exec' )) {
			throw new Exception ( 'Unable to execute shell commands using shell_exec(); the function is disabled.' );
		}
		
		// Check the video temporary directory
		$tmpDir = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'temporary' . DIRECTORY_SEPARATOR . 'vote_participant';
		if (! is_dir ( $tmpDir )) {
			if (! mkdir ( $tmpDir, 0777, true )) {
				throw new Exception ( 'Video temporary directory did not exist and could not be created.' );
			}
		}
		if (! is_writable ( $tmpDir )) {
			throw new Exception ( 'Video temporary directory is not writable.' );
		}
		
		// Get the video object
		if (is_numeric ( $part )) {
			$part = Engine_Api::_ ()->getItem ( 'vote_participant', $part );
		}
		
		if (! ($part instanceof Vote_Model_Participant)) {
			throw new Exception ( 'Argument was not a valid video' );
		}
		
		// Update to encoding status
		$part->status = 2;
		$part->ptype = 3;
		$part->save ();
		
		// Prepare information
		$owner = $part->getOwner ();
		$filetype = $part->code;
		
		//    // Pull video from storage system for encoding
		//    $storageObject = Engine_Api::_()->getItem('storage_file', $video->file_id);
		//    $originalPath = $storageObject->temporary();
		

		$originalPath = $tmpDir . DIRECTORY_SEPARATOR . $part->getIdentity () . '.' . $filetype;
		$outputPath = $tmpDir . DIRECTORY_SEPARATOR . $part->getIdentity () . '_vconverted.flv';
		$thumbPath = $tmpDir . DIRECTORY_SEPARATOR . $part->getIdentity () . '_vthumb.jpg';
		
		$videoCommand = $ffmpeg_path . ' ' . '-i ' . escapeshellarg ( $originalPath ) . ' ' . '-ab 64k' . ' ' . '-ar 44100' . ' ' . '-qscale 5' . ' ' . '-vcodec flv' . ' ' . '-f flv' . ' ' . '-r 25' . ' ' . '-s 480x386' . ' ' . '-v 2' . ' ' . '-y ' . escapeshellarg ( $outputPath ) . ' ' . '2>&1';
		
		// Prepare output header
		$output = PHP_EOL;
		$output .= $originalPath . PHP_EOL;
		$output .= $outputPath . PHP_EOL;
		$output .= $thumbPath . PHP_EOL;
		
		// Prepare logger
		$log = null;
		//if( APPLICATION_ENV == 'development' ) {
		$log = new Zend_Log ();
		$log->addWriter ( new Zend_Log_Writer_Stream ( APPLICATION_PATH . '/temporary/log/vote_participant.log' ) );
		//}
		

		// Execute video encode command
		$videoOutput = $output . $videoCommand . PHP_EOL . shell_exec ( $videoCommand );
		
		// Log
		if ($log) {
			$log->log ( $videoOutput, Zend_Log::INFO );
		}
		
		// Check for failure
		$success = true;
		
		// Unsupported format
		if (preg_match ( '/Unknown format/i', $videoOutput ) || preg_match ( '/Unsupported codec/i', $videoOutput ) || preg_match ( '/patch welcome/i', $videoOutput ) || preg_match ( '/Audio encoding failed/i', $videoOutput ) || ! is_file ( $outputPath ) || filesize ( $outputPath ) <= 0) {
			$success = false;
			$part->status = 3;
		} 

		// This is for audio files
		else if (preg_match ( '/video:0kB/i', $videoOutput )) {
			$success = false;
			$part->status = 5;
		}
		
		// Failure
		if (! $success) {
			
			$exceptionMessage = '';
			
			$db = $part->getTable ()->getAdapter ();
			$db->beginTransaction ();
			try {
				$part->save ();
				
				// notify the owner
				$translate = Zend_Registry::get ( 'Zend_Translate' );
				$language = (! empty ( $owner->language ) && $owner->language != 'auto' ? $owner->language : null);
				$notificationMessage = '';
				
				if ($part->status == 3) {
					$exceptionMessage = 'Video format is not supported by FFMPEG.';
					$notificationMessage = $translate->translate ( sprintf ( 'Video conversion failed. Video format is not supported by FFMPEG. Please try %1$sagain%2$s.', '', '' ), $language );
				} else if ($part->status == 5) {
					$exceptionMessage = 'Audio-only files are not supported.';
					$notificationMessage = $translate->translate ( sprintf ( 'Video conversion failed. Audio files are not supported. Please try %1$sagain%2$s.', '', '' ), $language );
				} else {
					$exceptionMessage = 'Unknown encoding error.';
				}
				
				Engine_Api::_ ()->getDbtable ( 'notifications', 'activity' )->addNotification ( $owner, $owner, $part, 'vote_participant_processed_failed', array ('message' => $notificationMessage, 'message_link' => Zend_Controller_Front::getInstance ()->getRouter ()->assemble ( array ('controller' => 'video', 'action' => 'manage' ), 'vote_general', true ) ) );
				
				$db->commit ();
			} catch ( Exception $e ) {
				$videoOutput .= PHP_EOL . $e->__toString () . PHP_EOL;
				if ($log) {
					$log->write ( $e->__toString (), Zend_Log::ERR );
				}
				$db->rollBack ();
			}
			
			// Write to additional log in dev
			if (APPLICATION_ENV == 'development') {
				file_put_contents ( $tmpDir . '/' . $part->getIdentity () . '.txt', $videoOutput );
			}
			
			throw new Exception ( $exceptionMessage );
		} 

		// Success
		else {
			// Get duration of the video to caculate where to get the thumbnail
			if (preg_match ( '/Duration:\s+(.*?)[.]/i', $videoOutput, $matches )) {
				list ( $hours, $minutes, $seconds ) = preg_split ( '[:]', $matches [1] );
				$duration = ceil ( $seconds + ($minutes * 60) + ($hours * 3600) );
			} else {
				$duration = 0; // Hmm
			}
			
			// Log duration
			if ($log) {
				$log->log ( 'Duration: ' . $duration, Zend_Log::INFO );
			}
			
			// Fetch where to take the thumbnail
			$thumb_splice = $duration / 2;
			
			// Thumbnail proccess command
			$thumbCommand = $ffmpeg_path . ' ' . '-i ' . escapeshellarg ( $outputPath ) . ' ' . '-f image2' . ' ' . '-ss ' . $thumb_splice . ' ' . '-v 2' . ' ' . '-y ' . escapeshellarg ( $thumbPath ) . ' ' . '2>&1';
			
			// Process thumbnail
			$thumbOutput = $output . $thumbCommand . PHP_EOL . shell_exec ( $thumbCommand );
			
			// Log thumb output
			if ($log) {
				$log->log ( $thumbOutput, Zend_Log::INFO );
			}
			
			// Check output message for success
			$thumbSuccess = true;
			if (preg_match ( '/video:0kB/i', $thumbOutput )) {
				$thumbSuccess = false;
			}
			
			// Resize thumbnail
			if ($thumbSuccess) {
				$image = Engine_Image::factory ();
				$image->open ( $thumbPath )->resize ( 120, 240 )->write ( $thumbPath )->destroy ();
			}
			
			// Save video and thumbnail to storage system
			$params = array ('parent_id' => $part->getIdentity (), 'parent_type' => $part->getType (), 'user_id' => $part->user_id );
			
			$db = $part->getTable ()->getAdapter ();
			$db->beginTransaction ();
			
			try {
				$videoFileRow = Engine_Api::_ ()->storage ()->create ( $outputPath, $params );
				if ($thumbSuccess) {
					$thumbFileRow = Engine_Api::_ ()->storage ()->create ( $thumbPath, $params );
				}
				$db->commit ();
			
			} catch ( Exception $e ) {
				$db->rollBack ();
				
				// delete the files from temp dir
				unlink ( $originalPath );
				unlink ( $outputPath );
				if ($thumbSuccess) {
					unlink ( $thumbPath );
				}
				
				$part->status = 7;
				$part->save ();
				
				// notify the owner
				$translate = Zend_Registry::get ( 'Zend_Translate' );
				$notificationMessage = '';
				$language = (! empty ( $owner->language ) && $owner->language != 'auto' ? $owner->language : null);
				if ($part->status == 7) {
					$notificationMessage = $translate->translate ( sprintf ( 'Video conversion failed. You may be over the site upload limit.  Try %1$suploading%2$s a smaller file, or delete some files to free up space.', '', '' ), $language );
				}
				Engine_Api::_ ()->getDbtable ( 'notifications', 'activity' )->addNotification ( $owner, $owner, $part, 'vote_participant_processed_failed', array ('message' => $notificationMessage, 'message_link' => Zend_Controller_Front::getInstance ()->getRouter ()->assemble ( array ('controller' => 'video', 'action' => 'manage' ), 'vote_general', true ) ) );
				
				throw $e; // throw
			}
			
			// Video processing was a success!
			// Save the information
			$part->file_id = $videoFileRow->file_id;
			if ($thumbSuccess) {
				$part->photo_id = $thumbFileRow->file_id;
			}
			$part->duration = $duration;
			$part->status = 1;
			$part->save ();
			
			// delete the files from temp dir
			unlink ( $originalPath );
			unlink ( $outputPath );
			unlink ( $thumbPath );
			
			//      // Delete the original version in the storage system
			//      $storageObject->delete();
			

			// insert action in a seperate transaction if video status is a success
			$actionsTable = Engine_Api::_ ()->getDbtable ( 'actions', 'activity' );
			$db = $actionsTable->getAdapter ();
			$db->beginTransaction ();
			
			try {
				// new action
				//        $action = $actionsTable->addActivity($owner, $part, 'vote_participant_new');
				//        if ($action) {
				//          $actionsTable->attachActivity($action, $part);
				//        }
				

				// notify the owner
				Engine_Api::_ ()->getDbtable ( 'notifications', 'activity' )->addNotification ( $owner, $owner, $part, 'vote_participant_processed' );
				
				$db->commit ();
			
			} catch ( Exception $e ) {
				$db->rollBack ();
				throw $e; // throw
			}
		}
	}
}